package org.example.Interceptor;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.example.util.LogUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Map;
import java.util.Properties;


@Intercepts({
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})
})
@Slf4j
@Component
public class MybatisLogInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 执行方法
        Object result = invocation.proceed();
        // 获取MapperStatement对象，获取到sql的详细方法
        Object realTarget =  realTarget(invocation.getTarget());
        // 获取metaObject对象
        MetaObject metaObject = SystemMetaObject.forObject(realTarget);
        // 获取MappedStatement对象
        MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        // 获取方法的全类名称
        String methodFullName = ms.getId();
        // 判断是否需要日志记录的方法
        Map<String,Object> map = LogUtils.verifyRecordLog(methodFullName);
        if(!map.isEmpty() && (boolean)map.get("isRecord")){
            Statement statement;
            // 获取方法参数
            Object[] args = invocation.getArgs();
            Object firstArg = args[0];
            if(Proxy.isProxyClass(firstArg.getClass())){
                statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement");

            }else{
                statement = (Statement)firstArg;
            }
            MetaObject metaObject1 = SystemMetaObject.forObject(statement);
            // 获取statement对象（sql语法已经构建完毕）
            statement = (Statement) metaObject1.getOriginalObject();
            // 获取sql语句
            String sql = statement.toString();
            String saveSql = LogUtils.getSaveLogSql(methodFullName, (String) map.get("desc"),sql);
            if(StringUtils.isNotBlank(saveSql)){
                Connection connection = statement.getConnection();
                if(connection.isReadOnly()){
                    // 当前事务时只读事务，则重新用不同的Connection对象
                    Connection mysqlConnection = LogUtils.getMysqlConnection();
                    if(mysqlConnection != null){
                        try{
                            mysqlConnection.createStatement().execute(saveSql);
                        }catch (Exception e){
                            e.printStackTrace();
//                            log.error("拦截器记录日志出错！",e);
                        }finally {
                            mysqlConnection.close();
                        }
                    }
                }else {
                    connection.createStatement().execute(saveSql);
                }
            }
        }
        return result;
    }



    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties prop) {

    }

    /**
     * <p>
     * 获得真正的处理对象,可能多层代理.
     * </p>
     */
    @SuppressWarnings("unchecked")
    public static <T> T realTarget(Object target) {
        if (Proxy.isProxyClass(target.getClass())) {
            MetaObject metaObject = SystemMetaObject.forObject(target);
            return realTarget(metaObject.getValue("h.target"));
        }
        return (T) target;
    }
}
